#include <fstream>
#include <iostream>
#include <string>
#include <vector>

// Use filesystem version 2 for compatibility with newer boost versions.
#ifndef BOOST_FILESYSTEM_VERSION
    #define BOOST_FILESYSTEM_VERSION 2
#endif

#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>

#include "Tracker.h"

/**
 * The main routine starting up the whole application.
 */
int main( int argc, char** argv )
{
	using namespace std;

	/**********************************************************************************************************
	 *
	 * process command line options
	 *
	 **********************************************************************************************************/

	// process user parameter
	namespace po = boost::program_options; // since the namespace is far to big we use a shortcut here
	po::options_description desc( "Options:" );

	desc.add_options()
		( "help,h", "Prints this help message" )
		( "version,v", "Prints the version information" )
		( "input,i", po::value< std::string >(), "input dti file" )
		( "mask,m", po::value< std::string >(), "input mask file" )
		( "step,s", po::value< float >(), "step size" )
		( "length,l", po::value< int >(), "min length" )
		( "fa,f", po::value< float >(), "min fa" )
		( "smoothness,w", po::value< float >(), "weighting of the incoming vector" );

	po::positional_options_description p;
	p.add( "input", -1 );

	boost::program_options::variables_map optionsMap;
	try
	{
		po::store( po::command_line_parser( argc, argv ).options( desc ).positional( p ).run(), optionsMap );
	}
	catch( const po::error &e )
	{
		std::cerr << e.what() << std::endl;
		return false;
	}

	po::notify( optionsMap );

	// print usage information if command line asks for help.
	if( optionsMap.count( "help" ) )
	{
		std::cout << "track this and that." << std::endl
				  << std::endl
				  << "Usage: track [OPTION]... [FILE]..." << std::endl
				  << std::endl
				  << desc << std::endl
				  << std::endl;
		return 0;
	}
	else if( optionsMap.count( "version" ) )
	{
		//printVersion();
		return 0;
	}

	/**********************************************************************************************************
	 *
	 *
	 *
	 **********************************************************************************************************/
	int minLength = 30;
	float minFA = 0.2;
	float stepSize = 1.0;
	float smoothness = 0.0;

	if( optionsMap.count( "step" ) )
	{
		stepSize = optionsMap["step"].as< float >();
		if ( stepSize < 0.0 || stepSize > 2.0 ) stepSize = 1.0;
	}

	if( optionsMap.count( "fa" ) )
	{
		minFA = optionsMap["fa"].as< float >();
		if ( minFA < 0.01 || minFA > 0.99 ) minFA = 0.2;
	}

	if( optionsMap.count( "length" ) )
	{
		minLength = optionsMap["length"].as< int >();
		if ( minLength < 0 ) minLength = 30;
	}

	if( optionsMap.count( "smoothness" ) )
	{
		smoothness = optionsMap["smoothness"].as< float >();
		if ( smoothness < 0 ) smoothness = 0.0;
	}

	Tracker* t = new Tracker( minLength, minFA, stepSize, smoothness );

	if( optionsMap.count( "mask" ) )
	{
		t->loadMask( optionsMap["mask"].as< std::string >() );
	}

	// take the input file name and create the bvec and bval file names by erasing .nii and .gz and
	// adding .bvec and .bval, that means those files need to have the same name before .nii.gz to be
	// found properly

	if( optionsMap.count( "input" ) )
	{
		vector< string >filenames;
		string filename = optionsMap["input"].as< std::string >();

		filenames.push_back( filename );
		filenames.push_back( filename );
		filenames.push_back( filename );

		string suffix = boost::filesystem::path( filenames[0] ).extension();

		if( suffix == ".gz" )
		{
			filenames[1].resize( filenames[1].length() - 3 );
			filenames[2].resize( filenames[2].length() - 3 );
			suffix = boost::filesystem::path( filenames[1] ).extension();
		}
		filenames[1].resize( filenames[1].length() - 4 );
		filenames[1] += ".bvec";
		filenames[2].resize( filenames[2].length() - 4 );
		filenames[2] += ".bval";

		if ( !t->load( filenames ) )
		{
			exit( 0 );
		}

	}
	else
	{
		cout << "*** Error, no input ***" << endl;
		exit( 0 );
	}

	t->fitTensors();
	//t->saveTensors();
	t->computeTrace();
	t->computeFA();
	//t->saveFA();
	t->computeEigen();
	//t->saveEvec();
	t->startTracking();
	t->saveFibs();
}
